package cn.jbolt.core.service.base;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.jbolt.core.base.JBoltMsg;
import cn.jbolt.core.base.JBoltPageSize;
import cn.jbolt.core.base.TreeCheckIsParentNode;
import cn.jbolt.core.bean.JsTreeBean;
import cn.jbolt.core.cache.JBoltUserCache;
import cn.jbolt.core.common.enums.JBoltSystemLogType;
import cn.jbolt.core.db.sql.DBType;
import cn.jbolt.core.db.sql.Sql;
import cn.jbolt.core.db.sql.SqlExpress;
import cn.jbolt.core.db.util.JBoltTxKit;
import cn.jbolt.core.model.base.JBoltBaseModel;
import cn.jbolt.core.para.IJBoltParaValidator;
import cn.jbolt.core.para.JBoltParaValidator;
import cn.jbolt.core.para.jbolttablemenufilter.JBoltTableMenuFilter;
import cn.jbolt.core.service.JBoltSystemLogService;
import cn.jbolt.core.util.JBoltArrayUtil;
import cn.jbolt.core.util.JBoltDateUtil;
import cn.jbolt.core.util.JBoltListMap;
import com.jfinal.aop.Inject;
import com.jfinal.kit.*;
import com.jfinal.log.Log;
import com.jfinal.plugin.activerecord.*;
import com.jfinal.plugin.activerecord.Record;
import com.jfinal.upload.UploadFile;

import java.io.File;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Service底层通用封装
 *
 * @ClassName: CommonService
 * @author: JFinal学院-小木 QQ：909854136
 * @date: 2019年12月10日
 */
public abstract class JBoltCommonService implements IJBoltService,IJBoltParaValidator {
	protected static final Log LOG = Log.getLog(JBoltCommonService.class);
	@Inject
	private JBoltSystemLogService systeLogService;
	protected static final BigDecimal ZERO_BIGDECIMAL = new BigDecimal("0");
	protected static final String TRUE = Sql.TRUE;
	protected static final String SEPARATOR = File.separator;
	protected static final String TRUE_STR = "true";
	protected static final String FALSE = Sql.FALSE;
	protected static final String FALSE_STR = "false";
	protected static final String IS_DEFAULT = "is_default";
	protected static final String ENABLE = "enable";
	protected static final String ID = "id";
	protected static final String PID = "pid";
	protected static final String IS_DELETED = "is_deleted";
	protected static final String NAME = "name";
	protected static final String TYPE = "type";
	protected static final String TYPE_ID = "type_id";
	protected static final String TYPE_KEY = "type_key";
	protected static final String SN = "sn";
	protected static final String REMARK = "remark";
	protected static final String CONTENT = "content";
	protected static final String SORT_RANK = "sort_rank";
	protected static final String VALUE = "value";
	protected static final String TEXT = "text";
	protected static final String ZERO_STR = "0";
	protected static final int ZERO = 0;
	protected static final String KEY_COMMA = ",";
	protected static final String KEY_UNDERLINE = "_";
	protected static final String KEY_JS_TREE_TYPE = "js_tree_type";
	protected static final String KEY_ITEMS = "items";
	protected static final String KEY_PARENT = "parent";
	protected static final String KEY_PARENT_OPENED = "parent_opened";
	protected static final String KEY_NODE = "node";
	protected static final String KEY_ROOT = "root";
	protected static final String KEY_DATA = "data";
	protected static final String ASC = "asc";
	protected static final String DESC = "desc";
	protected static final String KEY_ROOT_OPENED = "root_opened";

	protected static final String SQL_TPL_KEY_MYPARAS = "myparas";
	protected static final String SQL_TPL_KEY_PRIMARYKEY = "primaryKey";
	protected static final String SQL_TPL_KEY_CUSTOM_COMPARE = "customCompare";
	protected static final String SQL_TPL_KEY_COLUMNS = "columns";
	protected static final String SQL_TPL_KEY_OR = "or";
	protected static final String SQL_TPL_KEY_ORCLE_RANDOMROWNUM = "randomRownum";
	protected static final String SQL_TPL_KEY_TABLE = "table";
	protected static final String SQL_TPL_KEY_SUFFIX_ID = "_id";
	protected static final String SQL_TPL_KEY_ORDER_COLUMNS = "orderColumns";
	protected static final String SQL_TPL_KEY_ORDER_TYPES = "orderTypes";

	protected static final String SQL_TPL_COMMON_OPTIONLIST = "common.optionlist";
	protected static final String SQL_TPL_COMMON_LIST = "common.list";
	protected static final String SQL_TPL_COMMON_DELETE = "common.delete";
	protected static final String SQL_TPL_COMMON_COUNT = "common.count";
	protected static final String SQL_TPL_COMMON_FIRST = "common.first";
	protected static final String SQL_TPL_COMMON_FIRSTRAND = "common.firstrand";
	protected static final Object[] NULL_PARA_ARRAY = new Object[0];
	protected static final int[] NONE_BATCH_RESULT = new int[0];
	protected static final long LONG_1 = 1l;
	protected static final long LONG_0 = 0l;
	protected static final long LONG_MINUS_1 = -1l;
	protected static final int  INT_1 = 1;
	protected static final int INT_0 = 0;
	protected static final int INT_MINUS_1 = -1;

	protected String dataSourceConfigName;// 数据源配置名
	protected String databaseName;// 数据库名称
	protected String schema;// 数据库模式名称
	protected String tableName;// 主表名
	protected String dbType;// 数据库类型
	protected String primaryKey;// 主键
	protected String idGenMode;// 主键策略
	protected int systemLogTargetType;// 主键策略

	/**
	 * 获取日志关联类型
	 *
	 * @return
	 */
	protected abstract int systemLogTargetType();
	/**
	 * 获取数据源配置名称
	 *
	 * @return
	 */
	protected abstract String dataSourceConfigName();

	/**
	 * 获取数据库名称 没有返回null
	 * @return
	 */
	protected abstract String database();

	/**
	 * 获取数据库模式名称 没有返回null
	 * @return
	 */
	protected abstract String schema();

	/**
	 * 获取数据库类型
	 *
	 * @return
	 */
	protected abstract String dbType();

	/**
	 * 获取主键名称
	 *
	 * @return
	 */
	protected abstract String primaryKey();

	/**
	 * 获取主键策略
	 *
	 * @return
	 */
	protected abstract String idGenMode();

	/**
	 * 成功消息默认返回值
	 */
	protected static final Ret SUCCESS = Ret.ok(JBoltMsg.SUCCESS);
	/**
	 * 执行成功但是有警告
	 */
	protected static final Ret WARN = Ret.create().set("state","warn").set("msg","warning");
	/**
	 * 默认空KV
	 */
	protected static final Kv KV_EMPTY = Kv.create();
	/**
	 * 默认空OKV
	 */
	protected static final Okv OKV_EMPTY = Okv.create();
	/**
	 * 设置失败返回消息 默认设置需要回滚事务 但是 只有执行的方法设置了事务拦截才有效
	 * @return
	 */
	protected Ret fail() {
		return fail(JBoltMsg.FAIL,true);
	}
	/**
	 * 设置失败返回消息 默认设置需要回滚事务 但是 只有执行的方法设置了事务拦截才有效
	 *
	 * @param msg
	 * @return
	 */
	protected Ret fail(String msg) {
		return fail(msg,true);
	}


	/**
	 * 设置警告 意思是执行成功了 但是中间有警告 底层调用的就是success
	 * warn和success一样 没有事务回滚 只有fail有
	 * @param msg
	 * @return
	 */
	protected Ret warn(String msg) {
		return Ret.create().set("state","warn").set("msg",msg);
	}

	/**
	 * 设置警告 意思是执行成功了 但是中间有警告 底层调用的就是success
	 * warn和success一样 没有事务回滚 只有fail有
	 * @param msg
	 * @param data
	 * @return
	 */
	protected Ret warn(String msg,Object data) {
		return Ret.create().set("state","warn").set("msg",msg).set("data",data);
	}

	/**
	 * 设置失败返回消息
	 *
	 * @param msg
	 * @param isTxNeedCallback 是否需要回滚事务 但是 只有执行的方法设置了事务拦截才有效
	 * @return
	 */
	protected Ret fail(String msg,boolean isTxNeedCallback) {
		return fail(msg,isTxNeedCallback,null);
	}

	/**
	 * 设置失败返回消息
	 *
	 * @param msg
	 * @param isTxNeedCallback 是否需要回滚事务 但是 只有执行的方法设置了事务拦截才有效
	 * @param data
	 * @return
	 */
	protected Ret fail(String msg,boolean isTxNeedCallback,Object data) {
		if(isTxNeedCallback){
			JBoltTxKit.setNeedRollback(true);
			JBoltTxKit.setErrorMsg(msg);
		}
		return Ret.fail(msg).set("data",data);
	}

	/**
	 * 自动判断返回值
	 *
	 * @param success
	 * @return
	 */
	protected Ret ret(boolean success) {
		return success ? SUCCESS : fail(JBoltMsg.FAIL);
	}

	/**
	 * 过滤返回值 model专用
	 *
	 * @param ret
	 * @param columns
	 * @return
	 */
	protected Ret filterModelRet(Ret ret, String... columns) {
		if (columns == null) {
			return ret;
		}
		JBoltBaseModel<? extends JBoltBaseModel<?>> myModel = ret.getAs("data");
		if (columns.length == 1) {
			ret.set("data", myModel.get(columns[0]));
		} else {
			Kv kv = Kv.create();
			for (String column : columns) {
				kv.set("column", myModel.get(column));
			}
			ret.set("data", kv);
		}
		return ret;
	}

	/**
	 * 设置成功返回消息
	 *
	 * @param msg
	 * @return
	 */
	protected Ret success(String msg) {
		return Ret.ok(msg);
	}

	/**
	 * 设置成功返回消息 设置返回data
	 *
	 * @param data
	 * @return
	 */
	protected Ret successWithData(Object data) {
		return Ret.ok().set("data", data);
	}

	/**
	 * 设置成功返回值带着数据和消息
	 *
	 * @param data
	 * @param msg
	 * @return
	 */
	protected Ret success(Object data, String msg) {
		return Ret.ok(msg).set("data", data);
	}

	/**
	 * 添加save日志
	 * @param targetId
	 * @param userId
	 * @param modelName
	 * @param append
	 */
	protected void addSaveSystemLog(Object targetId, Long userId, String modelName,String append) {
		addSaveSystemLog(targetId, userId, systemLogTargetType(), modelName,append);
	}
	/**
	 * 添加save日志
	 * @param targetId
	 * @param userId
	 * @param modelName
	 */
	protected void addSaveSystemLog(Object targetId, Long userId, String modelName) {
		addSaveSystemLog(targetId, userId, systemLogTargetType(), modelName);
	}

	/**
	 * 调用日志服务 添加日志信息 操作类型是Save
	 *
	 * @param targetId   关联操作目标数据的ID
	 * @param userId     操作人
	 * @param targetType 操作目标的ID
	 * @param modelName  操作目标的具体数据的名字
	 */
	protected void addSaveSystemLog(Object targetId, Long userId, int targetType, String modelName) {
		addSaveSystemLog(targetId, userId, targetType, modelName, null);
	}
	/**
	 * 调用日志服务 添加日志信息 操作类型是Save
	 *
	 * @param targetId   关联操作目标数据的ID
	 * @param userId     操作人
	 * @param targetType 操作目标的ID
	 * @param modelName  操作目标的具体数据的名字
	 * @param append     额外信息
	 */
	protected void addSaveSystemLog(Object targetId, Long userId, int targetType, String modelName,String append) {
		addSystemLog(targetId, userId, JBoltSystemLogType.SAVE.getValue(), targetType, modelName, append);
	}

	/**
	 * 调用日志服务 添加日志信息 操作类型是Update
	 *
	 * @param targetId   关联操作目标数据的ID
	 * @param userId     操作人
	 * @param modelName  操作目标的具体数据的名字
	 */
	protected void addUpdateSystemLog(Object targetId, Long userId, String modelName) {
		addUpdateSystemLog(targetId, userId, systemLogTargetType(), modelName);
	}

	/**
	 * 调用日志服务 添加日志信息 操作类型是Update
	 *
	 * @param targetId   关联操作目标数据的ID
	 * @param userId     操作人
	 * @param modelName  操作目标的具体数据的名字
	 * @param append     额外信息
	 */
	protected void addUpdateSystemLog(Object targetId, Long userId, String modelName,String append) {
		addUpdateSystemLog(targetId, userId, systemLogTargetType(), modelName,append);
	}
	/**
	 * 调用日志服务 添加日志信息 操作类型是Update
	 *
	 * @param targetId   关联操作目标数据的ID
	 * @param userId     操作人
	 * @param targetType 操作目标的ID
	 * @param modelName  操作目标的具体数据的名字
	 */
	protected void addUpdateSystemLog(Object targetId, Long userId, int targetType, String modelName) {
		addUpdateSystemLog(targetId, userId, targetType, modelName, null);
	}

	/**
	 * 调用日志服务 添加日志信息 操作类型是Update
	 *
	 * @param targetId   关联操作目标数据的ID
	 * @param userId     操作人
	 * @param targetType 操作目标的ID
	 * @param modelName  操作目标的具体数据的名字
	 * @param append     额外信息
	 */
	protected void addUpdateSystemLog(Object targetId, Long userId, int targetType, String modelName, String append) {
		addSystemLog(targetId, userId, JBoltSystemLogType.UPDATE.getValue(), targetType, modelName, append);
	}

	/**
	 * 调用日志服务 添加日志信息 操作类型是Delete
	 *
	 * @param targetId   关联操作目标数据的ID
	 * @param userId     操作人
	 * @param modelName  操作目标的具体数据的名字
	 * @param append     额外信息
	 */
	protected void addDeleteSystemLog(Object targetId, Long userId,String modelName,String append) {
		addDeleteSystemLog(targetId, userId, systemLogTargetType(), modelName,append);
	}
	/**
	 * 调用日志服务 添加日志信息 操作类型是Delete
	 *
	 * @param targetId   关联操作目标数据的ID
	 * @param userId     操作人
	 * @param modelName  操作目标的具体数据的名字
	 */
	protected void addDeleteSystemLog(Object targetId, Long userId,String modelName) {
		addDeleteSystemLog(targetId, userId, systemLogTargetType(), modelName);
	}
	/**
	 * 调用日志服务 添加日志信息 操作类型是Delete
	 *
	 * @param targetId   关联操作目标数据的ID
	 * @param userId     操作人
	 * @param targetType 操作目标的ID
	 * @param modelName  操作目标的具体数据的名字
	 */
	protected void addDeleteSystemLog(Object targetId, Long userId, int targetType, String modelName) {
		addDeleteSystemLog(targetId, userId,targetType, modelName, null);
	}
	/**
	 * 调用日志服务 添加日志信息 操作类型是Delete
	 *
	 * @param targetId   关联操作目标数据的ID
	 * @param userId     操作人
	 * @param targetType 操作目标的ID
	 * @param modelName  操作目标的具体数据的名字
	 * @param append     额外信息
	 */
	protected void addDeleteSystemLog(Object targetId, Long userId, int targetType, String modelName,String append) {
		addSystemLog(targetId, userId, JBoltSystemLogType.DELETE.getValue(), targetType, modelName, append);
	}



	/**
	 * 调用日志服务 添加日志信息 操作类型是Recover
	 *
	 * @param targetId   关联操作目标数据的ID
	 * @param userId     操作人
	 * @param modelName  操作目标的具体数据的名字
	 * @param append     额外信息
	 */
	protected void addRecoverSystemLog(Object targetId, Long userId,String modelName,String append) {
		addRecoverSystemLog(targetId, userId, systemLogTargetType(), modelName,append);
	}
	/**
	 * 调用日志服务 添加日志信息 操作类型是Recover
	 *
	 * @param targetId   关联操作目标数据的ID
	 * @param userId     操作人
	 * @param modelName  操作目标的具体数据的名字
	 */
	protected void addRecoverSystemLog(Object targetId, Long userId,String modelName) {
		addRecoverSystemLog(targetId, userId, systemLogTargetType(), modelName);
	}
	/**
	 * 调用日志服务 添加日志信息 操作类型是Recover
	 *
	 * @param targetId   关联操作目标数据的ID
	 * @param userId     操作人
	 * @param targetType 操作目标的ID
	 * @param modelName  操作目标的具体数据的名字
	 */
	protected void addRecoverSystemLog(Object targetId, Long userId, int targetType, String modelName) {
		addRecoverSystemLog(targetId, userId,targetType, modelName, null);
	}
	/**
	 * 调用日志服务 添加日志信息 操作类型是Recover
	 *
	 * @param targetId   关联操作目标数据的ID
	 * @param userId     操作人
	 * @param targetType 操作目标的ID
	 * @param modelName  操作目标的具体数据的名字
	 * @param append     额外信息
	 */
	protected void addRecoverSystemLog(Object targetId, Long userId, int targetType, String modelName,String append) {
		addSystemLog(targetId, userId, JBoltSystemLogType.RECOVER.getValue(), targetType, modelName, append);
	}

	/**
	 * 调用日志服务 添加日志信息
	 *
	 * @param targetId   关联操作目标数据的ID
	 * @param userId     操作人
	 * @param type       操作类型
	 * @param modelName  操作目标的具体数据的名字
	 */
	protected void addSystemLog(Object targetId, Long userId, int type,String modelName) {
		addSystemLog(targetId, userId, type, systemLogTargetType(), modelName, null);
	}
	/**
	 * 调用日志服务 添加日志信息
	 *
	 * @param targetId   关联操作目标数据的ID
	 * @param userId     操作人
	 * @param type       操作类型
	 * @param targetType 操作目标的ID
	 * @param modelName  操作目标的具体数据的名字
	 */
	protected void addSystemLog(Object targetId, Long userId, int type, int targetType, String modelName) {
		addSystemLog(targetId, userId, type, targetType, modelName, null);
	}

	/**
	 * 调用日志服务 添加日志信息
	 *
	 * @param targetId   关联操作目标数据的ID
	 * @param userId     操作人
	 * @param type       操作类型
	 * @param targetType 操作目标的ID
	 * @param modelName  操作目标的具体数据的名字
	 * @param append     额外信息
	 */
	protected void addSystemLog(Object targetId, Long userId, int type, int targetType, String modelName,
			String append) {
		if(notOk(targetType)) {
			throw new RuntimeException("service需要实现systemLogTargetType() 并且有返回值");
		}
		String userName = JBoltUserCache.me.getName(userId);
		String userUsername = JBoltUserCache.me.getUserName(userId);
		StringBuilder title = new StringBuilder();
		title.append("<span class='text-danger'>[").append(userName).append("(").append(userUsername).append(")")
				.append("]</span>").append(systeLogService.typeName(type))
				.append(systeLogService.targetTypeName(targetType)).append("<span class='text-danger'>[")
				.append(modelName).append("]</span>");
		if (StrKit.notBlank(append)) {
			title.append(append);
		}
		// 调用底层方法
		addSystemLogWithTitle(targetId, userId, type, targetType, title.toString());
	}

	/**
	 * 调用日志服务 添加日志信息
	 *
	 * @param targetId
	 * @param userId
	 * @param type
	 * @param targetType
	 * @param title
	 */
	protected void addSystemLogWithTitle(Object targetId, Long userId, int type, int targetType, String title) {
		if(notOk(targetType)) {
			throw new RuntimeException("service需要实现systemLogTargetType() 并且有返回值");
		}
		String userName = JBoltUserCache.me.getName(userId);
		systeLogService.saveLog(type, targetType, targetId, title, 0, userId, userName);
	}

	/**
	 * 转换为Record tree
	 *
	 * @param allList
	 * @param idColumn
	 * @param pidColumn
	 * @param checkIsParentFunc
	 * @return
	 */
	public List<Record> convertToRecordTree(List<Record> allList, String idColumn, String pidColumn,
											TreeCheckIsParentNode<Record> checkIsParentFunc) {
		if (notOk(allList)) {
			return Collections.emptyList();
		}
		List<Record> parents = new ArrayList<Record>();
		Record m;
		for (int i = 0; i < allList.size(); i++) {
			m = allList.get(i);
			m.put("eachLevel", 1);
			if (checkIsParentFunc.isParent(m)) {
				parents.add(m);
				allList.remove(m);
				i--;
			}
		}
		if (parents.size() > 0 && allList.size() > 0) {
			processTreeNodes(allList, parents, idColumn, pidColumn);
		}
		return parents;
	}

	/**
	 * 处理tree节点
	 *
	 * @param allList
	 * @param parents
	 * @param idColumn
	 * @param pidColumn
	 */
	private void processTreeNodes(List<Record> allList, List<Record> parents, String idColumn, String pidColumn) {
		JBoltListMap<String, Record> map = new JBoltListMap<String, Record>();
		for (Record m : allList) {
			map.addItem("p_" + m.get(pidColumn), m);
		}
		for (Record p : parents) {
			processTreeSubNodes(map, p, idColumn);
		}

	}

	/**
	 * 设置循环层级 根据父节点的eachLevel处理 树形类表结构才有用
	 *
	 * @param record
	 * @param parentLevel
	 */
	public void processEachLevelByParentLevel(Record record, Integer parentLevel) {
		record.put("eachLevel", parentLevel == null ? 1 : parentLevel + 1);
	}

	/**
	 * 递归处理tree子节点
	 *
	 * @param map
	 * @param m
	 * @param idColumn
	 */
	private void processTreeSubNodes(JBoltListMap<String, Record> map, Record m, String idColumn) {
		List<Record> items = map.get("p_" + m.get(idColumn));
		if (items != null && items.size() > 0) {
			for (Record item : items) {
				processEachLevelByParentLevel(item, m.getInt("eachLevel"));
				processTreeSubNodes(map, item, idColumn);
			}
		}
		m.set(KEY_ITEMS, items);
	}

	/**
	 * 空分页数据返回值
	 *
	 * @param <T>
	 * @param pageSize
	 * @return
	 */
	public <T> Page<T> emptyPage(Integer pageSize) {
		if (notOk(pageSize)) {
			pageSize = JBoltPageSize.PAGESIZE_ADMIN_LIST;
		}
		return new Page<T>(Collections.emptyList(), 1, pageSize, 0, 0);
	}

	/**
	 * 添加拼接like
	 *
	 * @param column
	 */
	protected String columnLike(String column) {
		if (notOk(column)) {
			return " like ''";
		}
		if (column.indexOf("'") != -1) {
			column = column.replace("'", "''");
		}
		return " like '%" + column + "%'";
	}

	/**
	 * 更新同级删除数据之后数据的排序
	 *
	 * @param sortRank
	 */
	protected void updateSortRankAfterDelete(String table, Integer sortRank) {
		updateSortRankAfterDelete(table, null, sortRank);
	}

	/**
	 * 更新同级删除数据之后数据的排序
	 *
	 * @param params
	 * @param sortRank
	 */
	protected void updateSortRankAfterDelete(String table, Okv params, Integer sortRank) {
		if (params == null || params.isEmpty()) {
			Db.use(dataSourceConfigName()).update("update " + table + " set sort_rank=sort_rank-1 where sort_rank>?",
					sortRank);
		} else {
			StringBuilder sb = new StringBuilder();
			sb.append(" 1=1 ");
			int size = params.size();
			Object[] paramArray = new Object[size + 1];
			int i = 0;
			for (Object key : params.keySet()) {
				sb.append(" and ").append(key.toString()).append("=? ");
				paramArray[i] = params.get(key.toString());
				i++;
			}
			paramArray[size] = sortRank;
			Db.use(dataSourceConfigName()).update(
					"update " + table + " set sort_rank=sort_rank-1 where " + sb.toString() + " and sort_rank>?",
					paramArray);
		}
	}

	/**
	 * 查询一个字段
	 *
	 * @param sql
	 * @return
	 */
	protected <T> T queryColumn(Sql sql) {
		if (sql.isPrepared()) {
			return queryColumn(sql, sql.getWhereValues());
		}
		return queryColumn(sql.toSql());
	}

	/**
	 * 查询一个字段
	 *
	 * @param <T>
	 * @param sql
	 * @param paras
	 * @return
	 */
	protected <T> T queryColumn(Sql sql, Object... paras) {
		return queryColumn(sql.toSql(), paras);
	}

	/**
	 * 查询一个字段
	 *
	 * @param <T>
	 * @param sql
	 * @return
	 */
	protected <T> T queryColumn(String sql) {
		return Db.use(dataSourceConfigName()).queryColumn(sql);
	}

	/**
	 * 查询一个字段
	 *
	 * @param <T>
	 * @param sql
	 * @param paras
	 * @return
	 */
	protected <T> T queryColumn(String sql, Object... paras) {
		return Db.use(dataSourceConfigName()).queryColumn(sql, paras);
	}

	protected Integer queryInt(Sql sql) {
		if (sql.isPrepared()) {
			return queryInt(sql, sql.getWhereValues());
		}
		return queryInt(sql.toSql());
	}

	protected Integer queryInt(Sql sql, Object... paras) {
		return queryInt(sql.toSql(), paras);
	}

	protected Integer queryInt(String sql) {
		return Db.use(dataSourceConfigName()).queryInt(sql);
	}

	protected Integer queryInt(String sql, Object... paras) {
		return Db.use(dataSourceConfigName()).queryInt(sql, paras);
	}

	protected Long queryLong(Sql sql, Object... paras) {
		return queryLong(sql.toSql(), paras);
	}

	protected Long queryLong(String sql) {
		return Db.use(dataSourceConfigName()).queryLong(sql);
	}

	protected Long queryLong(String sql, Object... paras) {
		return Db.use(dataSourceConfigName()).queryLong(sql, paras);
	}

	protected int update(Sql sql) {
		if (sql.isPrepared()) {
			return update(sql, sql.getWhereValues());
		}
		return update(sql.toSql());
	}

	protected int update(Sql sql, Object... paras) {
		return update(sql.toSql(), paras);
	}

	protected int update(String sql) {
		return Db.use(dataSourceConfigName()).update(sql);
	}

	protected int update(String sql, Object... paras) {
		return Db.use(dataSourceConfigName()).update(sql, paras);
	}

	protected int delete(Sql sql) {
		if (sql.isPrepared()) {
			return delete(sql, sql.getWhereValues());
		}
		return delete(sql.toSql());
	}

	protected int delete(Sql sql, Object... paras) {
		return delete(sql.toSql(), paras);
	}

	protected int delete(String sql, Object... paras) {
		return Db.use(dataSourceConfigName()).delete(sql, paras);
	}

	protected <T> List<T> query(String sql, Object... paras) {
		return Db.use(dataSourceConfigName()).query(sql, paras);
	}

	protected <T> List<T> query(String sql) {
		return Db.use(dataSourceConfigName()).query(sql);
	}

	protected <T> List<T> query(Sql sql) {
		if (sql.isPrepared()) {
			return query(sql.toSql(), sql.getWhereValues());
		}
		return query(sql.toSql());
	}

	protected <T> List<T> query(Sql sql, Object... paras) {
		return query(sql.toSql(), paras);
	}
	/**
	 * 存在有效参数
	 */
	@Override
	public boolean hasOk(Object... params) {
		return JBoltParaValidator.hasOk(params);
	}

	/**
	 * 存在无效参数
	 */
	@Override
	public boolean hasNotOk(Object... params) {
		return JBoltParaValidator.hasNotOk(params);
	}
	/**
	 * 判断Object参数有效性
	 *
	 * @param param
	 */
	@Override
	public boolean isOk(Object param) {
		return JBoltParaValidator.isOk(param);
	}

	/**
	 * 判断Object参数是否无效
	 */
	@Override
	public boolean notOk(Object param) {
		return JBoltParaValidator.notOk(param);
	}

	/**
	 * 判断record参数有效性
	 * @param record
	 */
	public boolean isOk(Record record) {
		return JBoltParaValidator.isOk(record);
	}

	/**
	 * 判断record参数是否无效
	 * @param record
	 */
	public boolean notOk(Record record) {
		return JBoltParaValidator.notOk(record);
	}
	/**
	 * 判断model参数有效性
	 * @param model
	 */
	public boolean isOk(Model<? extends Model<?>> model) {
		return JBoltParaValidator.isOk(model);
	}

	/**
	 * 判断record参数是否无效
	 * @param model
	 */
	public boolean notOk(Model<? extends Model<?>> model) {
		return JBoltParaValidator.notOk(model);
	}
	/**
	 * 判断Object参数为Null
	 * @param param
	 */
	public boolean isNull(Object param){
		return JBoltParaValidator.isNull(param);
	}
	/**
	 * 判断Object参数不为Null
	 */
	public boolean notNull(Object param){
		return JBoltParaValidator.notNull(param);
	}

	/**
	 * 判断上传文件是图片
	 *
	 * @param contentType
	 * @return
	 */
	@Override
	public boolean isImage(String contentType) {
		return JBoltParaValidator.isImage(contentType);
	}

	/**
	 * 判断上传文件不是图片
	 *
	 * @param contentType
	 * @return
	 */
	@Override
	public boolean notImage(String contentType) {
		return JBoltParaValidator.notImage(contentType);
	}

	/**
	 * 判断上传文件类型不是图片
	 *
	 * @param file
	 */
	@Override
	public boolean notImage(UploadFile file) {
		return JBoltParaValidator.notImage(file);
	}

	/**
	 * 判断上传文件类型是否为图片
	 *
	 * @param file
	 */
	@Override
	public boolean isImage(UploadFile file) {
		return JBoltParaValidator.isImage(file);
	}

	/**
	 * 判断Object[]数组类型数据是否正确
	 *
	 * @param param
	 * @return
	 */
	@Override
	public boolean isOk(Object[] param) {
		return JBoltParaValidator.isOk(param);
	}

	/**
	 * 判断Object[]数组类型数据不正确
	 *
	 * @param param
	 * @return
	 */
	@Override
	public boolean notOk(Object[] param) {
		return JBoltParaValidator.notOk(param);
	}

	/**
	 * 判断Serializable[]数组类型数据不正确
	 *
	 * @param param
	 * @return
	 */
	public boolean notOk(Serializable[] param) {
		return JBoltParaValidator.notOk(param);
	}

	/**
	 * 判断Integer[]数组类型数据不正确
	 *
	 * @param param
	 * @return
	 */
	public boolean notOk(Integer[] param) {
		return JBoltParaValidator.notOk(param);
	}

	/**
	 * 转为startTime
	 *
	 * @param startTime
	 * @return
	 */
	public Object toStartTime(Date startTime) {
		return toDateTime(JBoltDateUtil.HHmmssTo000000Str(startTime));
	}

	/**
	 * 转为endTime
	 *
	 * @param endTime
	 * @return
	 */
	public Object toEndTime(Date endTime) {
		return toDateTime(JBoltDateUtil.HHmmssTo235959Str(endTime));
	}

	/**
	 * 转为表达式处理date
	 *
	 * @param dateTime
	 * @return
	 */
	public Object toDateTime(String dateTime) {
		if (notOk(dateTime)) {
			return null;
		}
		if (dbType().equals(DBType.ORACLE)) {
			return new SqlExpress(String.format("TO_DATE('%s','yyyy-mm-dd hh24:mi:ss')", dateTime));
		}
		return dateTime;
	}

	/**
	 * 时间格式化 YMD
	 * null代表今天
	 * @return
	 */
	public String toDateYMD(Date date) {
		if (date == null) date = new Date();
		return DateUtil.format(date, JBoltDateUtil.YMD);
	}


	@Override
	public boolean notExcel(UploadFile file) {
		return JBoltParaValidator.notExcel(file);
	}

	@Override
	public boolean isExcel(UploadFile file) {
		return JBoltParaValidator.isExcel(file);
	}

	/**
	 * 删除webapp下的文件
	 *
	 * @param filePath
	 * @return
	 */
	public Ret deleteWebappFile(String filePath) {
		if (notOk(filePath)) {
			return fail("文件地址有误");
		}
		if (!FileUtil.isAbsolutePath(filePath)) {
			filePath = FileUtil.normalize(PathKit.getWebRootPath() + File.separator + filePath);
		}
		if (!FileUtil.exist(filePath)) {
			return fail("文件不存在");
		}
		boolean success = FileUtil.del(new File(filePath));
		return ret(success);
	}

	protected void processRecordJsTree(List<JsTreeBean> treeBeans, List<Record> jstreeMs, int openLevel,
			int currentLevel, String idColumn, String pidColumn, String textColumn, String enableColumn, String orderColumn,String[] keys,
			Object selectedId, boolean hasRoot, boolean needOriginData) {
		if (notOk(jstreeMs)) {
			return;
		}

		if(orderColumn!=null && orderColumn.trim().length()==0){
			orderColumn = null;
		}
		if(orderColumn == null){
			if(jstreeMs.get(0).getColumns().containsKey(SORT_RANK)){
				orderColumn = SORT_RANK;
			}else if(jstreeMs.get(0).getColumns().containsKey((SORT_RANK.toUpperCase()))){
				orderColumn = SORT_RANK.toUpperCase();
			}
		}
		boolean opened = false;
		boolean keysIsOk = isOk(keys);
		Object idValue;
//		Object pidValue;
		String type;
		JsTreeBean treeBean;
		Integer order = 0;
		int index = 0;
		for (Record m : jstreeMs) {
			opened = openLevel == -1 || false;
			boolean selected = false;

//			pidValue=m.get(pidColumn);
			if (opened == false && currentLevel <= openLevel) {
				opened = true;
			}
			idValue = m.get(idColumn);
			if (keysIsOk && idValue != null && idValue.toString().equals("0") == false
					&& JBoltArrayUtil.contains(keys, idValue.toString())) {
				selected = false;
				opened = true;
			}
			if (selectedId != null && idValue.toString().equals(selectedId.toString())) {
				selected = true;
				opened = true;
			}

			type = m.get(KEY_JS_TREE_TYPE);
			if (notOk(type)) {
				type = KEY_PARENT;
			}
			if (type.equals(KEY_NODE) == false && opened) {
				type = KEY_PARENT_OPENED;
			}
			treeBean = convertToJsTreeBean(m, opened, selected, type, idColumn, pidColumn, textColumn, enableColumn,
					hasRoot, needOriginData);
			if(orderColumn != null){
				order = m.getInt(orderColumn);
				if(order==null){
					treeBean.setOrder(0);
				}else{
					treeBean.setOrder(order);
				}
			}else{
				treeBean.setOrder(index);
				index++;
			}
			treeBeans.add(treeBean);
			processRecordJsTree(treeBeans, m.get(KEY_ITEMS), openLevel, (currentLevel + 1), idColumn, pidColumn,
					textColumn, enableColumn, orderColumn,keys, selectedId, hasRoot, needOriginData);
		}

	}

	public JsTreeBean convertToJsTreeBean(Record m, boolean opened, boolean selected, String type, String idColumn,
			String pidColumn, String textColumn, String enableColumn, boolean hasRoot, boolean needOriginData) {
		if (needOriginData) {
			return new JsTreeBean(m.get(idColumn), m.get(pidColumn), getEnableName(m, textColumn, enableColumn), opened,
					selected, type, hasRoot).setData(m);
		}
		return new JsTreeBean(m.get(idColumn), m.get(pidColumn), getEnableName(m, textColumn, enableColumn), opened,
				selected, type, hasRoot);
	}

	/**
	 * 树形结构上单独使用的
	 * @return
	 */
	private String getEnableName(Record m, String textColumn, String enableColumn) {
		String name = m.get(textColumn);
		String enableValue = m.get(enableColumn);
		Boolean enable = isOk(enableValue) && (Boolean.parseBoolean(enableValue) || enableValue.equals(TRUE));
		if(enable == null){
			return name;
		}
		return (enable != null && enable) ? name : (name + "[<span class='text-danger'>禁用</span>]");
	}

	protected void processRecordJsTreeType(List<Record> jstreeMs) {
		if (isOk(jstreeMs)) {
			List<Record> items;
			for (Record m : jstreeMs) {
				items = m.get(KEY_ITEMS);
				if (isOk(items)) {
					m.set(KEY_JS_TREE_TYPE, KEY_PARENT);
					processRecordJsTreeType(items);
				} else {
					m.set(KEY_JS_TREE_TYPE, KEY_NODE);
				}
			}
		}
	}

	/**
	 * 处理事务
	 *
	 * @param atom
	 * @return
	 */
	protected boolean tx(IAtom atom) {
		return Db.use(dataSourceConfigName()).tx(atom);
	}

	/**
	 * 处理事务
	 *
	 * @param transactionLevel
	 * @param atom
	 * @return
	 */
	protected boolean tx(int transactionLevel, IAtom atom) {
		return Db.use(dataSourceConfigName()).tx(transactionLevel, atom);
	}

	/**
	 * 处理sql 使用jboltTableMenuFilter构造
	 *
	 * @param sql
	 * @param jboltTableMenuFilter
	 * @param matchColumns
	 */
	protected void processJBoltTableMenuFilterSql(Sql sql, JBoltTableMenuFilter jboltTableMenuFilter,
			String... matchColumns) {
		if (jboltTableMenuFilter.getPaging()) {
			sql.page(jboltTableMenuFilter.getPageNumber(), jboltTableMenuFilter.getPageSize());
		}
		if (isOk(jboltTableMenuFilter.getSortColumn())) {
			sql.orderBy(jboltTableMenuFilter.getSortColumn(), jboltTableMenuFilter.getSortType());
		}
		if (isOk(jboltTableMenuFilter.getKeywords())) {
			if (jboltTableMenuFilter.getInclude()) {
				sql.likeMulti(jboltTableMenuFilter.getKeywords(), matchColumns);
			} else {
				sql.notLikeMulti(jboltTableMenuFilter.getKeywords(), matchColumns);
			}
		}
		if (isOk(jboltTableMenuFilter.getItems())) {
			jboltTableMenuFilter.getItems().forEach(item -> {
				if (item.getValue() != null && item.getValue().toString().length() > 0) {
					switch (item.getComparison()) {
					case "like":
						sql.like(item.getColumn(), item.getValue().toString());
						break;
					case "notLike":
						sql.notLike(item.getColumn(), item.getValue().toString());
						break;
					case "startWith":
						sql.startWith(item.getColumn(), item.getValue().toString());
						break;
					case "endWith":
						sql.endWith(item.getColumn(), item.getValue().toString());
						break;
					case "notStartWith":
						sql.notStartWith(item.getColumn(), item.getValue().toString());
						break;
					case "notEndWith":
						sql.notEndWith(item.getColumn(), item.getValue().toString());
						break;
					case "eq":
						sql.eq(item.getColumn(), item.getValue());
						break;
					case "noteq":
						sql.notEq(item.getColumn(), item.getValue());
						break;
					case "ge":
						sql.ge(item.getColumn(), item.getValue());
						break;
					case "gt":
						sql.gt(item.getColumn(), item.getValue());
						break;
					case "le":
						sql.le(item.getColumn(), item.getValue());
						break;
					case "lt":
						sql.lt(item.getColumn(), item.getValue());
						break;
					}
				} else {
					switch (item.getComparison()) {
					case "empty":
						sql.bracketLeft();
						sql.isNull(item.getColumn());
						sql.or();
						sql.eq(item.getColumn(), "");
						sql.bracketRight();
						break;
					case "notEmpty":
						sql.bracketLeft();
						sql.isNotNull(item.getColumn());
						sql.or();
						sql.notEq(item.getColumn(), "");
						sql.bracketRight();
						break;
					}
				}
			});
		}
	}


	public String[] splitByLineToArray(String str) {
		List<String> lines = splitByLineToList(str);
		if(isOk(lines)){
			return lines.toArray(new String[0]);
		}
		return null;
	}

	public List<String> splitByLineToList(String str) {
		if(notOk(str)){
			return null;
		}
		str = str.trim();
		String regex = "\\r?\\n";
		List<String> lines = Arrays.stream(str.split(regex))
				.map(String::trim)
				.filter(s -> !s.isEmpty())
				.collect(Collectors.toList());
		if(notOk(lines)){
			return null;
		}
		if(str.contains(",") || str.contains(" ")){
			String[] temps = null;
			List<String> tempList;
			List<String> rets = new ArrayList<>();
			String[] tempArr;
			String[] tempArr2;
			boolean has1=false;
			boolean has2=false;
			for(String line:lines){
				has1 = line.contains(",");
				has2 = line.contains(" ");
				if(has1&&!has2){
					temps =  JBoltArrayUtil.from(line);
				}else if(has2&!has1){
					temps =  JBoltArrayUtil.from(line," ");
				}else if(has1){
					tempList =new ArrayList<>();
					//第一步 用空格分
					tempArr = JBoltArrayUtil.from(line," ");
					if(isOk(tempArr)){
						//第二步 用逗号分
						for(String temp:tempArr){
							tempArr2 = JBoltArrayUtil.from(temp,",");
							if(isOk(tempArr2)){
								tempList.addAll(Arrays.asList(tempArr2));
							}
						}
					}
					if(isOk(tempList)){
						temps = tempList.toArray(new String[0]);
					}
				}else{
					temps = new String[]{line};
				}
				if(isOk(temps)){
					assert temps != null;
					rets.addAll(Arrays.asList(temps));
				}
			}
			return rets;
		}
		return lines;
	}

}
